#ifdef ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
// #define ESP32
#include <WiFiManager.h>
#include <PubSubClient.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <RX8010.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <TimeLib.h>
#include <esp_sleep.h> //睡眠相关
#define LED_PIN 32
#define AM2120_SDA 26
#define IRQ1_PIN 33
#define IRQ2_PIN 25
#define ADC_PIN 34
#define CARD_READY 35
#define KEY1 17
#define KEY2 16
RTC_DATA_ATTR int updateCount = 0;             // 睡眠后数据不清零8
RTC_DATA_ATTR char file_csv[30], file_txt[30]; // 睡眠后数据不清零8
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp.aliyun.com", 8 * 3600, 60000);
char msg[50];
char mqtt_server[] = "h3253k9765.oicp.vip";
// const char *mqtt_server = "192.168.3.210";
const char *mqttUser = "lu";
const char *mqttPassWd = "5891381";
const int port = 1883;
WiFiClient espClient;
PubSubClient client(espClient);
bool online;
bool update;
unsigned long lastTime = 0;
/* struct dateTime
{
    int year;
    int month;
    int date;
    int day;
    int hour;
    int minute;
    int second;
}; */
dateTime timeSync()
{
    dateTime dt;
    unsigned long epochTime;
    timeClient.update();
    epochTime = timeClient.getEpochTime();
    dt.year = year(epochTime);
    dt.month = month(epochTime);
    dt.date = day(epochTime);
    dt.day = weekday(epochTime);
    dt.hour = hour(epochTime);
    dt.minute = minute(epochTime);
    dt.second = second(epochTime);
    return dt;
}
bool wifiConnect()
{
    if (WiFi.status() != WL_CONNECTED)
    {
        WiFiManager wm;
        bool res;
        uint8_t macAddr[6];
        WiFi.macAddress(macAddr);
        char SSID[10];
        int i = 0;
        sprintf(SSID, "AP-%X%X", macAddr[4], macAddr[5]);
        if (digitalRead(KEY1) == 0)
        {
            delay(500);
            if (digitalRead(KEY1) == 0)
                wm.resetSettings();
        }
        wm.setTimeout(180);
        wm.setConnectTimeout(10);
        res = wm.autoConnect(SSID);
        WiFi.hostname("test"); // 连接后再设置host
        if (!res)
        {
            Serial.println("Failed to connect");
            return 0;
        }
        else
        {
            Serial.println("connected...yeey :)");
            return 1;
        }
    }
    else
        return 1;
}
void setupAM2120()
{
    pinMode(AM2120_SDA, OUTPUT);
    digitalWrite(AM2120_SDA, HIGH);
}
int readAM2120(float *fhumidity, float *ftemp)
{
    pinMode(AM2120_SDA, OUTPUT);
    digitalWrite(AM2120_SDA, LOW);
    delay(5);
    uint8_t data[5];
    unsigned long time = micros();
    pinMode(AM2120_SDA, INPUT_PULLUP);
    delayMicroseconds(25);
    time = micros();
    while (digitalRead(AM2120_SDA))
    {
        if (micros() - time > 100) // 延时可能不对，需确认
            break;
    }
    time = micros();
    while (!digitalRead(AM2120_SDA)) // 应答低电平
        if (micros() - time > 90)
            return -1;
    time = micros();
    while (digitalRead(AM2120_SDA)) // 应答低电平
        if (micros() - time > 90)
            return -2;
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 8; j++)
        {
            time = micros();
            while (!digitalRead(AM2120_SDA))
                if (micros() - time > 55)
                    return -3; // timeout waiting for start of data bit.
            delayMicroseconds(40);
            data[i] = (data[i] << 1) + (digitalRead(AM2120_SDA) ? 1 : 0);
            time = micros();
            // Wait for data line to go low
            while (digitalRead(AM2120_SDA))
                if (micros() - time > 55)
                    return -4; // Timeout waiting for end of data bit
        }
    if ((uint8_t)(data[0] + data[1] + data[2] + data[3]) != data[4])
        return -3; // checksum failure.
    uint16_t humidity = ((int)data[0] << 8) + ((int)data[1]);
    uint16_t temp = ((int)data[2] << 8) + ((int)data[3]);
    *fhumidity = (float)(humidity) / 10.0f;
    *ftemp = (float)(temp & 0x7FFF) / 10.0f;
    if (temp & 0x8000)
        *ftemp = -*ftemp;
    digitalWrite(AM2120_SDA, HIGH);
    return 0;
}

void callback(char *topic, byte *payload, unsigned int length)
{
    String msg = "";
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    for (int i = 0; i < length; i++)
    {
        msg += (char)payload[i];
    }
    Serial.println(msg);
}

void reconnect()
{
    int i = 0;
    // Loop until we're reconnected
    while (!client.connected())
    {
        Serial.print("Attempting MQTT connection...");
        // Attempt to connect
        if (client.connect("arduinoClient", mqttUser, mqttPassWd, 0, 0, 0, 0, 0))
        {
            Serial.println("connected");
        }
        else
        {
            Serial.print("failed, rc=");
            Serial.print(client.state());
            Serial.println(" try again in 1 seconds");
            delay(1000);
        }
        i++;
        if (i > 5)
            break;
    }
}

// 订阅
void subScribe()
{
    client.subscribe("AM2120/Set");
}
void appendFile(fs::FS &fs, const char *path, const char *message)
{
    if (digitalRead(35) == 0)
    {
        File file = fs.open(path, FILE_APPEND);
        if (!file)
        {
            Serial.println("Failed to open file for appending");
            return;
        }
        if (file.println(message))
        {
            //   Serial.println("Message appended");
        }
        else
        {
            Serial.println("Append failed");
        }
        file.close();
    }
}
void interruptCallBack()
{
    update = 1;
}
void setup()
{
    Wire.begin(); // join I2C bus (RX8018_ADDRess optional for master)
    Serial.begin(115200);
    dateTime now = getDateTime();
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, 0);
    pinMode(CARD_READY, INPUT);
    pinMode(KEY1, INPUT);
    pinMode(KEY2, INPUT);

    if (digitalRead(CARD_READY) == 0)
    {
        if (!SD.begin())
        {
            Serial.println("Card Mount Failed");
            return;
        }
    }
    else
        Serial.println("NO Card");

    //   Serial.printf("ESP32 update times: %d \n", ++updateCount);
    wifiConnect();
    client.setServer(mqtt_server, port);
    client.setCallback(callback);
    esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 0);
    sprintf(msg, "%03d:ESP wakeup reason is :%d", updateCount, esp_sleep_get_wakeup_cause());
    Serial.println(msg);
    setupAM2120();
    if (esp_sleep_get_wakeup_cause() == 0)
    {
        Serial.print("TimeSync & Set Rx8010...");
        sprintf(file_txt, "/Record%02d%02d-%02d%02d%02d.txt", now.month, now.date, now.hour, now.minute, now.second);
        sprintf(file_csv, "/Record%02d%02d-%02d%02d%02d.csv", now.month, now.date, now.hour, now.minute, now.second);
        appendFile(SD, file_csv, "Date,Time,humi,temp,voltage,updatetimes");
        resetRX8010();
        disableFOUT();
        dateTime dt = timeSync();
        setTime(dt);
        delay(3000);
        disableFCT();
        setFCT(IRQ1, 120, 0b010); // 1 minutes
        Serial.println("OK");
    }
}
void loop()
{
    float temp;
    float humidity;
    int err;
    double vol = 0;
    //    char fileName[20];
    dateTime now = getDateTime();
    online = wifiConnect();
    if (online)
        reconnect();
    client.loop();
    for (int i = 0; i < 5; i++)
    {
        err = readAM2120(&humidity, &temp);
        if (err != 0)
        {
            Serial.print("AM2120 Error: ");
            Serial.println(err);
            delay(1000);
        }
        else
            break;
    }
    //   sprintf(fileName, "/Record%02d%02d.txt", now.month, now.date);
    vol = double(analogRead(34)) * 4 / 2270;
    sprintf(msg, "{\"time\":\"%02d:%02d:%02d\",\"humi\":%.1f,\"temp\":%.1f,\"vol\":%.2f,\"times\":%d}",
            now.hour, now.minute, now.second, humidity, temp, vol, updateCount++);
    Serial.print("T[AM2120]:");
    Serial.println(msg);
    if (online)
        client.publish("AM2120", msg);
    appendFile(SD, file_txt, msg);
    sprintf(msg, "%02d-%02d-%02d,%02d:%02d:%02d,%.1f,%.1f,%.2f,%d", now.year, now.month, now.date, now.hour, now.minute, now.second, humidity, temp, vol, updateCount);
    //      sprintf(fileName, "/Record%02d%02d.csv", now.month, now.date);
    appendFile(SD, file_csv, msg);
    digitalWrite(LED_PIN, 1);
    Serial.println("Deep sleep...");
    esp_deep_sleep_start();
}
